package sc.common.perform;

import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

import junit.framework.Assert;

import org.joda.time.DateTimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

public class PerformTester {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    public PerformTester(int runTimes, int threadCount) {
        Assert.assertTrue("runTimes必须大于0", runTimes > 0);
        Assert.assertTrue("threadCount必须大于0", threadCount > 0);
        this.runTimes = runTimes;
        this.threadCount = threadCount;
        this.executor = new ThreadPoolTaskExecutor();
        this.executor.setCorePoolSize(threadCount);
        this.executor.initialize();
    }

    int runTimes;
    int threadCount;
    int errorCount;
    long consumeTime;

    ThreadPoolTaskExecutor executor;

    public <T> void run(Callable<T> callable) {
        long begin = DateTimeUtils.currentTimeMillis();
        if (threadCount == 1) {
            runWithMultiThread(callable);
        } else {
            runWithMultiThread(callable);
        }
        long end = DateTimeUtils.currentTimeMillis();
        consumeTime = end - begin;
        logger.info(toString());
        executor.destroy();
    }

    <T> void runWithOneThread(Callable<T> callable) {
        for (int i = 0; i != runTimes; i++) {
            try {
                callable.call();
            } catch (Exception e) {
                errorCount++;
            }
        }
    }

    <T> void runWithMultiThread(Callable<T> callable) {
        ArrayList<FutureTask<T>> futureTasks = new ArrayList<FutureTask<T>>();
        for (int i = 0; i != runTimes; i++) {
            FutureTask<T> futureTask = new FutureTask<T>(callable);
            futureTasks.add(futureTask);
            executor.execute(futureTask);
        }
        for (FutureTask<T> futureTask : futureTasks) {
            try {
                futureTask.get();
            } catch (Exception e) {
                errorCount++;
                logger.error("", e);
            }
        }

    }

    @Override
    public String toString() {
        long runPerSecond = (consumeTime != 0) ? runTimes * 1000 / consumeTime : -1;
        long millisPerRun = (runTimes != 0) ? consumeTime / runTimes : -1;
        return "PerformTester [runTimes=" + runTimes + ", threadCount=" + threadCount + ", errorCount=" + errorCount
                + ", consumeTime=" + consumeTime + ", runPerSecond=" + runPerSecond + ", millisPerRun=" + millisPerRun
                + "]";
    }

    public static <T> void run(int performTestTimes, int runTimes, int threadCount, Callable<T> callable) {
        for (int i = 0; i < performTestTimes; i++) {
            new PerformTester(runTimes, threadCount).run(callable);
        }
    }

    public static <T> void run(int runTimes, int threadCount, Callable<T> callable) {
        run(1, runTimes, threadCount, callable);
    }
}
